package com.liliudong.rommath.biz.sys.service.impl;


import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.liliudong.rommath.biz.sys.entity.SysUser;
import com.liliudong.rommath.biz.sys.mapper.SysUserMapper;
import com.liliudong.rommath.biz.sys.param.SysAuthLoginParam;
import com.liliudong.rommath.biz.sys.param.SysUserAddParam;
import com.liliudong.rommath.biz.sys.param.SysUserPageParam;
import com.liliudong.rommath.biz.sys.result.SysAuthLoginResult;
import com.liliudong.rommath.biz.sys.result.SysUserPageResult;
import com.liliudong.rommath.biz.sys.service.SysMenuService;
import com.liliudong.rommath.biz.sys.service.SysRoleService;
import com.liliudong.rommath.biz.sys.service.SysUserRoleService;
import com.liliudong.rommath.biz.sys.service.SysUserService;
import com.liliudong.rommath.common.consts.RommathConst;
import com.liliudong.rommath.common.exception.BizException;
import com.liliudong.rommath.plugin.storage.service.StorageService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;

import static com.liliudong.rommath.biz.sys.entity.table.SysDeptTableDef.SYS_DEPT;
import static com.liliudong.rommath.biz.sys.entity.table.SysRoleTableDef.SYS_ROLE;
import static com.liliudong.rommath.biz.sys.entity.table.SysUserRoleTableDef.SYS_USER_ROLE;
import static com.liliudong.rommath.biz.sys.entity.table.SysUserTableDef.SYS_USER;
import static com.mybatisflex.core.query.QueryMethods.groupConcat;

/**
 * 系统用户 服务层实现。
 *
 * @author liudong.li
 * @since 1.0.0
 */
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    private final SysRoleService sysRoleService;

    private final SysMenuService sysMenuService;

    private final StorageService storageService;

    private final SysUserRoleService sysUserRoleService;

    @Override
    public SysAuthLoginResult login(SysAuthLoginParam sysAuthLoginParam) {
        String account = sysAuthLoginParam.getAccount();
        String password = sysAuthLoginParam.getPassword();

        SysUser sysUser = queryChainIgnoreDataPermission()
                .where(SYS_USER.ACCOUNT.eq(account))
                .one();
        if (Objects.isNull(sysUser)) {
            throw new BizException("账户不存在");
        }

        SysUser.Status userStatus = sysUser.getStatus();
        if (Objects.equals(userStatus, SysUser.Status.LOCKED)) {
            throw new BizException("账户已锁定");
        }

        // 加密密码
        if (!DigestUtil.bcryptCheck(password, sysUser.getPassword())) {
            throw new BizException("密码错误");
        }
        StpUtil.login(sysUser.getId());

        // 设置角色和权限
        List<String> roleCodes = sysRoleService.selectCodesByUserIdIgnoreDataPermission(sysUser.getId());
        List<Integer> dataScopes = sysRoleService.selectDataScopesByUserIdIgnoreDataPermission(sysUser.getId());
        List<String> permissions = sysMenuService.selectPermissionsByUseIdIgnoreDataPermission(sysUser.getId());

        // 缓存登录账户信息
        StpUtil.getTokenSession()
                .set(RommathConst.DATA_SCOPES, dataScopes)
                .set(RommathConst.DEPT_CODE, sysUser.getDeptCode())
                .set(SaSession.USER, sysUser.toCurrentUser())
                .set(SaSession.PERMISSION_LIST, permissions)
                .set(SaSession.ROLE_LIST, roleCodes);

        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        long expiresTime = System.currentTimeMillis() + tokenInfo.getTokenTimeout();

        SysAuthLoginResult sysAuthLoginResult = new SysAuthLoginResult();
        sysAuthLoginResult.setUsername(sysUser.getAccount());
        if (StrUtil.isNotEmpty(sysUser.getAvatar())) {
            sysAuthLoginResult.setAvatar(sysUser.getAvatar());
        }

        sysAuthLoginResult.setNickname(sysUser.getNickname());
        sysAuthLoginResult.setRoles(roleCodes);
        sysAuthLoginResult.setPermissions(permissions);
        sysAuthLoginResult.setAccessToken(tokenInfo.getTokenValue());
        sysAuthLoginResult.setExpires(expiresTime);
        return sysAuthLoginResult;
    }

    @Override
    public void add(SysUserAddParam param) {
        SysUser sysUser = SysUser.of(param);
        addOrModifyCheck(sysUser);

        // 加密密码
        String password = sysUser.getPassword();
        sysUser.setPassword(DigestUtil.bcrypt(password));
        save(sysUser);
        // 保存用户角色关联
        sysUserRoleService.saveBatchByUserId(sysUser.getId(), param.getRoleIdList());
    }

    @Override
    public Page<SysUserPageResult> queryPage(SysUserPageParam param) {
        String account = param.getAccount();
        SysUser.Status status = param.getStatus();
        String nickname = param.getNickname();
        Long page = param.getPage();
        Long pageSize = param.getPageSize();

        return queryChainIgnoreDataPermission()
                .select(SYS_USER.ID, SYS_USER.ACCOUNT, SYS_USER.NICKNAME, SYS_USER.STATUS, SYS_USER.CREATED_AT,
                        groupConcat(SYS_ROLE.NAME).as("roleNames"), SYS_DEPT.NAME.as("deptName"))
                .leftJoin(SYS_USER_ROLE).on(SYS_USER.ID.eq(SYS_USER_ROLE.USER_ID))
                .leftJoin(SYS_ROLE).on(SYS_USER_ROLE.ROLE_ID.eq(SYS_ROLE.ID))
                .leftJoin(SYS_DEPT).on(SYS_DEPT.CODE.eq(SYS_USER.DEPT_CODE))
                .where(SYS_USER.ACCOUNT.like(account, ObjectUtil.isNotEmpty(account)))
                .where(SYS_USER.STATUS.eq(status, ObjectUtil.isNotEmpty(status)))
                .where(SYS_USER.NICKNAME.like(nickname, ObjectUtil.isNotEmpty(nickname)))
                .groupBy(SYS_USER.ID)
                .pageAs(Page.of(page, pageSize), SysUserPageResult.class);
    }

    /**
     * 添加或修改检查
     *
     * @param sysUser sys用户
     */
    private void addOrModifyCheck(SysUser sysUser) {
        String account = sysUser.getAccount();
        boolean accountExists = queryChainIgnoreDataPermission()
                .where(SYS_USER.ACCOUNT.eq(account))
                .exists();
        if (accountExists) {
            throw new BizException("账号已存在");
        }
    }
}